#!/usr/bin/perl
#
# Copyright 2008-2010 Vadim Zhukov <persgray@gmail.com>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
#    list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
#    this list of conditions and the following disclaimer in the documentation
#    and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY AGAVA COMPANY ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
# EVENT SHALL THE AGAVA COMPANY OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and documentation are
# those of the authors and should not be interpreted as representing official
# policies, either expressed or implied, of the AGAVA Company.

use strict;
use bytes;

use Fcntl qw/:seek :mode/;
use Getopt::Std;
use POSIX qw/strftime/;
use RB::Dir;
use RB::Log;
use RB::Profile;
use RB::Util;

# For RB::Log
our $LOGPROGNAME = 'rb_fixinfo';

# For Getopt::Std
our $VERSION = "2.4";
$Getopt::Std::STANDARD_HELP_VERSION = 1;

sub HELP_MESSAGE {
        print <<EOU;
Usage:
rb_fixoinfo [-nv] [-d dir] [-m module] profile host [...]
    -d Specify directory where archives are kept (defaults to ~/dumps/rb)
    -m Specify module list (comma-separated) to rotate only
    -n Test mode: do not actually remove any files
    -v Verbose mode: print process of taking decision
Profile can be set as relative (to ~/.rb/) or absolute path.
It's not an error to specify unexisting module name in -m.
EOU
}


######################################
# Setting up options

my %CmdOptions;
$Getopt::Std::STANDARD_HELP_VERSION = 1;
getopts('d:m:nv', \%CmdOptions);


die "Profile was not given" unless @ARGV;

my $profilePath = shift @ARGV;
errx (1, "No profile given") unless $profilePath ne '';
if (substr($profilePath, 0, 1) ne '/') {
	$profilePath = $ENV{'HOME'}.'/.rb/'.$profilePath;
}

our $Profile = RB::Profile->new($profilePath);
our $Config = $Profile->{'global'};

# Set test mode flag
if ($CmdOptions{'n'}) {
        $Config->{'test_mode'} = 1;
}

# Set verbosity level
if ($CmdOptions{'v'}) {
        $Config->{'verbosity'} = 2;
}

# Override archive directory?
if ($CmdOptions{'d'}) {
        # No need to glob(), it's already coming from command line
        $Config->{'archive_dir'} = $CmdOptions{'d'};
} else {
        ($Config->{'archive_dir'}) = (glob $Config->{'archive_dir'});
}
if ($Config->{'archive_dir'} eq '') {
        $Config->{'archive_dir'} = '.';
}

my %OnlyModules;
if ($CmdOptions{'m'}) {
        %OnlyModules = map { ($_, 1) } (split ('\s*,\s*', $CmdOptions{'m'}));
}


######################################
# Routines

# Input: backup module and directory with (possibly) archives.
# Directory will be scanned recursively.
# Returns number of files fixed in directory.
sub fixInfoInDir($$) {
	my ($module, $dir) = (shift, shift);
	my $dh;
	unless (opendir($dh, $dir)) {
		logIt(0, "    Error: cannot open directory $dir: $!");
		return 0;
	}
	my $nFilesFixed = 0;
	logIt(1, "        Proceeding directory $dir");
	while (my $f = readdir $dh) {
		next if $f eq '.' or $f eq '..';
		my $path = $dir.'/'.$f;
		my @stat = stat $path;
		if (S_ISDIR($stat[2])) {
			$nFilesFixed += fixInfoInDir($module, $path);
			next;
		}

		next unless S_ISREG($stat[2]) and $f =~ /^[0-9]{8}\.info$/;

		my ($params, $nParamsFixed) =
		    processArchiveInfo($path, $module, 1);
		$nFilesFixed++ if $nParamsFixed;
	}
	closedir $dh;
	return $nFilesFixed;
}

# Input: host and module names
# Returns number of files fixed for the module.
sub fixModuleInfo($$) {
	my ($host, $moduleName) = (shift, shift);
	my $module = $Profile->{$moduleName};
	my $modulePath = $Config->{'archive_dir'}.'/'.$host.'/'.$moduleName;

	# Nothing to do
	unless (-d $modulePath) {
		logIt(1, "$modulePath doesn't exist or isn't a directory, ".
		    "skipping");
		return 0;
	}

	return fixInfoInDir($module, $modulePath);
}


######################################
# Begin

die "No hosts given" unless @ARGV;
openGlobalLog;
foreach my $host (map { lc } @ARGV) {
	logIt(1, "Proceeding host $host");

	my $hostFixedCount = 0;
	foreach my $moduleName (@{$Config->{'backup_modules'}}) {
		if(%OnlyModules and !exists $OnlyModules{$moduleName}) {
			logIt(1, "    Module $moduleName skipped due to -m ".
			    "command line option");
			next;
		}
		logIt(1, "    Proceeding module $moduleName");
		my $fixedCount = fixModuleInfo($host, $moduleName);
		$hostFixedCount += $fixedCount;
		logIt(1, "    Module $moduleName ended: $fixedCount files ".
		    "fixed");
	}

	logIt(1, "Host $host proceeded: $hostFixedCount files fixed");
}

